package componentes.basicos;

import java.util.ArrayList;
import java.util.Random;

/**
 * Classe que representa uma estrutura de memórioa genérica.
 * 
 * @author Gustavo Miguel, Cleisson Alves, Evandro Francisco, Lucas.
 */
public class Memoria {
    
    /* Atributos da classe */
    private int numEnderecos;
    private ArrayList<String> dados; 
    private int qtdInstrucoes;
    
    /**
     * Cria um objeto Memória com uma determinada quantidade de endereços.
     * @param numEnderecos Número de endereços na memória.
     */
    public Memoria(int numEnderecos) {
        dados = new ArrayList(numEnderecos);
        this.numEnderecos = numEnderecos;
        inicializaMemoria();
    }
    
    /**
     * Construtor que incializa uma memoria de capacidade 256KB.
     * 
     */
    public Memoria() {
        dados = new ArrayList(numEnderecos);
        this.numEnderecos = 64*4;
        inicializaMemoria();
    }
    
    /**
     * Metodo para retornar uma palavra contida em um determinado endereco.
     * @param pos Posição da memória.
     */
    public Palavra getConteudoEndereco(int pos) {
        String palavra = "";
        
        if(pos > 63999) {
            System.out.println("Posicao de memoria Invalida.");
            return null;
        }
        
        palavra = dados.get(pos);
        return new Palavra(palavra);
        
    }
    
    
    
    /**
     * Metodo para escrever em uma determinada posicao da memoria
     * @param p Palavra a ser escrita
     * @param pos Posição na memória onde a palavra será escrita.
     */
    public void escreveNaMemoria(Palavra p, int pos) {
        
        if(pos > 63999) {
            System.out.println("Posicao de memoria Invalida.");
            return;
        }
        
        String palavra = p.getPalavra();
        dados.add(pos,palavra.toString());
         
    }
    
    /**
     * Retorna o numero de enderecos da memoria.
     * @return Número de endereços.
     */
    public int getNumEnderecos() {
        return numEnderecos;
    }

    /**
     * Retorna a quantidade de instruções na memória.
     * @return Quantidade de instruções na memória.
     */
    public int getQtdInstrucoes() {
        return qtdInstrucoes;
    }

    /**
     * Retorna uma lista de Strings com a representação dos dados contidos 
     * na memória.
     * @return Dados contidos na memória.
     */
    public ArrayList<String> getDados() {
        return dados;
    }
    
    /**
     * Carrega uma lista passada como parâmetro contendo as Palavras que
     * serão escritas na memória. Elas serão escritas a partir da forma
     * com que estão dispostas na lista.
     * @param palavras Lista de Palavras que serão escritas na memoria.
     */
    public void carregaDados(ArrayList<Palavra> palavras) throws Exception {
        qtdInstrucoes = palavras.size();
        if(palavras.size()*4 > numEnderecos) {
            throw new Exception("Quantidade memoria indisponivel");
        }
        
        this.dados = new ArrayList<String>();
        
        for(int j = 0; j < palavras.size(); ++j) {
            
            String palavra = palavras.get(j).getPalavra();
            int posInicial = 0;
            int posFinal = 8;

            for(int i = 0; i < 4; ++i) {
                this.dados.add(palavra.substring(posInicial, posFinal));
                posInicial = posFinal;
                posFinal = posFinal+8;           
            }
        }
        
        if(this.dados.size() < numEnderecos) {
            for(int i = this.dados.size()-1; i < numEnderecos; ++i) {
                this.dados.add("00000000");
            }
        }
        
    }
    
    /**
     * Método para inicializar a memória com dados aleatórios.
     */
    public void inicializaMemoriaValAleatorios() {
        Random r = new Random();
        dados = new ArrayList<String>();
        for(int i = 0; i < numEnderecos; ++i) {
            int valor = r.nextInt(20);
            String binario = Integer.toBinaryString(valor);
            int limite = 8 - binario.length();
            for(int j = 0; j < limite; ++j) {
                binario = "0".concat(binario);
            }
            dados.add(binario);   
        }
        
        
    }
    
    /* Metodo Privados*/
    // Metodo privado para inicializar uma memoria.
    private void inicializaMemoria() {
        for(int i = 0; i < numEnderecos; ++i) {
            dados.add("00000000");            
        }
    }
    
} // Fim da classe.
